`include "cpu_def.vh"

module decode(
  input clk,
  input rst,

  // from if stage
  input        valid_i,
  input [31:0] pc_i   ,
  input [31:0] instr_i,
  input        ft_tlbre_i,
  input        ft_tlbi_i,
  input        ft_adel_i,
  input        br_pre_taken_i,
  input [31:0] br_pre_pc_i,

  // from forward path
  input [ 1:0] sel_rf_rdata_0_i ,  // forward data select
                                   //* 00: rdata from regfile
                                   //* 01: forward_data_ex
                                   //* 10: forward_data_wb
  input [ 1:0] sel_rf_rdata_1_i ,
  input [31:0] forward_data_ex_i,
  input [31:0] forward_data_wb_i,

  // regfile read channel
  output [ 4:0] rf_raddr_0_o,
  output [ 4:0] rf_raddr_1_o,
  input  [31:0] rf_rdata_0_i,
  input  [31:0] rf_rdata_1_i,

  // from hi lo forward path
  input [ 1:0] sel_hl_rdata_i,
  input [31:0] hl_rdata_ex_i ,
  input [31:0] hl_rdata_wb_i ,

  // hi lo read channel
  output [ 1:0] hl_raddr_o,
  input  [31:0] hl_rdata_i,

  // from cp0 forward path
  input [ 1:0] sel_cp0_rdata_i,
  input [31:0] cp0_rdata_ex_i,
  input [31:0] cp0_rdata_wb_i,

  // cp0 read channel
  output [ 2:0] cp0_rsel_o    ,
  output [ 4:0] cp0_rreg_num_o,
  input  [31:0] cp0_rdata_i   ,
  input  [31:0] cp0_rmask_i   ,

  // to id_ex_reg
  output                   valid_o          ,
  output [           31:0] pc_o             ,
  output                   br_pre_taken_o   ,
  output [           31:0] br_pre_pc_o      , 
  output [           31:0] rf_rdata_0_o     ,
  output [           31:0] rf_rdata_1_o     ,
  output [           31:0] hl_rdata_o       ,
  output [           31:0] cp0_rdata_o      ,
  output [            4:0] rs_o             ,
  output [            4:0] rt_o             ,
  output [            4:0] rd_o             ,
  output [            4:0] sa_o             ,             
  output [           31:0] simm_o           ,           
  output [           31:0] uimm_o           ,
  output [           31:0] addr_o           ,         
  output [           12:0] alu_op_o         ,
  output                   sel_alu_src_0_o  ,
  output [            1:0] sel_alu_src_1_o  ,
  output [            1:0] sel_rf_waddr_o   ,
  output [            1:0] sel_br_target_o  ,
  output                   sel_hl_wdata_ex_o,
  output                   read_mem_o       ,    
  output                   write_mem_o      ,   
  output                   branch_o         ,   
  output [`NR_LD_OP - 1:0] ld_op_o          ,       
  output [`NR_ST_OP - 1:0] st_op_o          ,       
  output [`NR_BR_OP - 1:0] br_op_o          ,   
  output                   mul_o            ,    
  output                   mul_sign_o       ,    
  output                   div_o            ,         
  output                   div_sign_o       ,
  output                   chk_ov_o         ,
  output [           31:0] br_target_o      ,
  output [           31:0] jp_target_o      ,

  output                   write_rf_o       ,
  output [            2:0] sel_rf_wdata_o   ,
  output [            1:0] write_hl_o       ,
  output                   sel_hl_wdata_o   ,
  output [            2:0] cp0_wsel_o       ,
  output                   cp0_wen_o        ,
  output                   ft_tlbre_o       ,
  output                   ft_tlbi_o        ,
  output                   ft_adel_o        ,
  output                   sys_o            ,
  output                   brk_o            ,
  output                   ri_o             ,
  output                   eret_o           ,
  output                   tlbp_o           ,
  output                   tlbr_o           ,
  output                   tlbwi_o          ,
  output                   tlbwr_o          ,
  
  // to forward path
  output [4:0] de_rs,
  output [4:0] de_rt,

  // to hazard
  output stall_req_de,

  // from ex
  input ex_ld_i,
  input ex_mul_i,
  
  // from wb
  input wb_ld_i,
  input wb_mul_i
);

  wire de_read_rs;
  wire de_read_rt;

  wire hl_ex_rel = sel_hl_rdata_i == 2'b01;
  wire hl_wb_rel = sel_hl_rdata_i == 2'b10;
  wire rs_ex_rel = sel_rf_rdata_0_i == 2'b01;
  wire rt_ex_rel = sel_rf_rdata_1_i == 2'b01;
  wire rs_wb_rel = sel_rf_rdata_0_i == 2'b10;
  wire rt_wb_rel = sel_rf_rdata_1_i == 2'b10;

  wire read_hl_after_mult = 
    ex_mul_i && hl_ex_rel ||
    wb_mul_i && hl_wb_rel ;
  wire read_rf_adter_mul =
    ex_mul_i && (rs_ex_rel || rt_ex_rel) ||
    wb_mul_i && (rs_wb_rel || rt_wb_rel) ;
  wire read_rf_after_ld = 
    (rs_ex_rel || rt_ex_rel) && ex_ld_i ||
    (rs_wb_rel || rt_wb_rel) && wb_ld_i ;

  assign stall_req_de = (read_hl_after_mult || read_rf_adter_mul || read_rf_after_ld) && valid_i;

  decoder mycpu_decoder(
    .instr(instr_i),

    .rf_raddr_0 (rf_raddr_0_o ),
    .rf_raddr_1 (rf_raddr_1_o ),
    .rs         (rs_o         ),
    .rt         (rt_o         ),
    .rd         (rd_o         ),
    .sa         (sa_o         ),
    .simm       (simm_o       ),
    .uimm       (uimm_o       ),
    .sel        (cp0_wsel_o   )
  );

  control mycpu_control(
    .clk  (clk  ),
    .rst  (rst  ),
    .instr(instr_i),

    .de_read_rs(de_read_rs),
    .de_read_rt(de_read_rt),

    .ex_alu_op       (alu_op_o         ),
    .ex_sel_alu_src_0(sel_alu_src_0_o  ),
    .ex_sel_alu_src_1(sel_alu_src_1_o  ),
    .ex_sel_rf_waddr (sel_rf_waddr_o   ),
    .ex_sel_br_target(sel_br_target_o  ),
    .sel_hl_wdata_ex (sel_hl_wdata_ex_o),
    .ex_read_mem     (read_mem_o       ),
    .ex_write_mem    (write_mem_o      ),
    .ex_branch       (branch_o         ),
    .ex_ld_op        (ld_op_o          ),
    .ex_st_op        (st_op_o          ),
    .ex_br_op        (br_op_o          ),
    .ex_mul          (mul_o            ),
    .ex_mul_sign     (mul_sign_o       ),
    .ex_div          (div_o            ),
    .ex_div_sign     (div_sign_o       ),
    .ex_chk_ov       (chk_ov_o         ),

    .wb_write_rf     (write_rf_o       ),
    .wb_sel_rf_wdata (sel_rf_wdata_o   ),
    .wb_write_hl     (write_hl_o       ),
    .sel_hl_wdata_wb (sel_hl_wdata_o   ),
    .wb_hl_raddr     (hl_raddr_o       ),
    .wb_cp0_wen      (cp0_wen_o        ),
    .wb_sys          (sys_o            ),
    .wb_brk          (brk_o            ),
    .wb_ri           (ri_o             ),
    .wb_eret         (eret_o           ),
    .wb_tlbp         (tlbp_o           ),
    .wb_tlbr         (tlbr_o           ),
    .wb_tlbwi        (tlbwi_o          ),
    .wb_tlbwr        (tlbwr_o          )
  );

  decode_branch mycpu_decode_branch(
    .instr(instr_i),
    .pc   (pc_i   ),

    .br_target(br_target_o),
    .jp_target(jp_target_o)
  );

  assign valid_o = valid_i;
  assign pc_o = pc_i;
  assign ft_tlbre_o = ft_tlbre_i;
  assign ft_tlbi_o = ft_tlbi_i;
  assign ft_adel_o = ft_adel_i;
  assign br_pre_taken_o = br_pre_taken_i;
  assign br_pre_pc_o = br_pre_pc_i;

  assign cp0_rsel_o     = cp0_wsel_o;
  assign cp0_rreg_num_o = rd_o;

  assign rf_rdata_0_o = sel_rf_rdata_0_i[1] ? 
                       (sel_rf_rdata_0_i[0] ? 32'd0             : forward_data_wb_i) :
                       (sel_rf_rdata_0_i[0] ? forward_data_ex_i : rf_rdata_0_i     ) ;
  assign rf_rdata_1_o = sel_rf_rdata_1_i[1] ? 
                       (sel_rf_rdata_1_i[0] ? 32'd0             : forward_data_wb_i) :
                       (sel_rf_rdata_1_i[0] ? forward_data_ex_i : rf_rdata_1_i     ) ;

  // assign addr_o = rf_rdata_0_o + simm_o;
  adder_32 mycpu_decode_adder (
    .A(rf_rdata_0_o),
    .B(simm_o),
    .C_IN(0),
    .S(addr_o),
    .C_OUT()
  );
  // adder32 mycpu_adder(
  //   .A(rf_rdata_0_o),
  //   .B(simm_o),
  //   .C(0),
  //   .S(addr_o),
  //   .C32()
  // );

  assign hl_rdata_o   = sel_hl_rdata_i[1] ? 
                       (sel_hl_rdata_i[0] ? 32'd0         : hl_rdata_wb_i) :
                       (sel_hl_rdata_i[0] ? hl_rdata_ex_i : hl_rdata_i   ) ;
  wire [31:0] raw_cp0_rdata = sel_cp0_rdata_i[1] ? 
                      (sel_cp0_rdata_i[0] ? 32'd0          : cp0_rdata_wb_i) :
                      (sel_cp0_rdata_i[0] ? cp0_rdata_ex_i : cp0_rdata_i   ) ;
  assign cp0_rdata_o = (raw_cp0_rdata & cp0_rmask_i) | (cp0_rdata_i & ~cp0_rmask_i);

  assign de_rs = de_read_rs ? rs_o : 5'd0;
  assign de_rt = de_read_rt ? rt_o : 5'd0;

endmodule
